# "Driver" for telnet acquisition of moorings data
#
# Author: David Goldsmith, Wash. State Dept. of Ecology, dgol461@ecy.wa.gov
# Original release date: < 9/15/2011

import os
import os.path as op
import sys
from math import floor
from telnetlib import Telnet as T
from socket import timeout
from smtplib import SMTP
import autogetdata_dev as GD
from time import *
import datetime as DT

# Global constants & initializations
logging = True
prompt = "S>"
prompt23 = "#>"
Max = 10
progress_code = ''
IPsPORTs = {"MUK01BR": ("166.140.38.221", 4001),
            "MUK01SR": ("166.140.38.221", 4002),
            "MCH01BR": ("166.140.38.222", 4001),
            "MCH01SR": ("166.140.38.222", 4002),
            "WPA13CR": ("166.130.17.242", 8001),
            "SQX01CF": ("166.140.038.220", 4001),
            "HQ": ("166.140.126.14", 4001)}
server = "ecylcyfsvrxfile"
dest = "\\\\" + server + "\\eap\\SHARED Files\\MWDataMgmt\\VSS_Mirror\\rt\\Telnet\\Output\\"

# Classes
class kill_timeout(timeout):
    def __init__(self):
        timeout.__init__(self)

# Functions
def figure_out_restart(date, time):
    now = DT.datetime(date.year, date.month, date.day,
                      time.hour, time.minute)
    dt = DT.timedelta(seconds=15*60)
    temp = now + dt
    minute = 15*floor(temp.minute/15.0)
    return DT.datetime(temp.year, temp.month, temp.day, temp.hour, int(minute))

def get_serial_ports(connection):
    connection.write("who\r\n")
    connection.read_until("\r\n", Max)
    tmp = connection.read_until("\r\n", Max)
    pids = []
    while "#>" not in tmp:
        tmp = connection.read_until("\r\n", Max)
        if "serial" in tmp:
            pids.append(tmp[0])
    return pids

def kill_serial_ports(connection, pids):
    for pid in pids:
        connection.write("kill %s\r\n"%pid)
        connection.read_until("\r\n", Max)

def report_failure(site, progress_code):
    msg = "Telnet data download from " + site + " failed with the following progress code:\n"
    msg += progress_code
    mailer = SMTP("165.151.24.51")
    mailer.sendmail("dgol461@ecy.wa.gov",
                   ["dgol461@ecy.wa.gov", "damo461@ecy.wa.gov", "ckre461@ecy.wa.gov"],
                   msg)
    mailer.quit()

#def driver(site, hours2sample=36):
site = sys.argv[1]
if site[:2]=="HQ":
    kind = site[2:]
    site = site[:2]
    Path = op.join(site, kind)
else:
    Path = site

if site == "WPA13CR":
    Max = 20

if len(sys.argv) > 2:
    hours2sample=float(sys.argv[2])
else:
    hours2sample=30

if len(sys.argv) > 3:
    offset=max(float(sys.argv[3]), 0)
else:
    offset = 0

if len(sys.argv) > 4:
    writeOut=int(sys.argv[4])
else:
    writeOut=1

ip, port = IPsPORTs[site]
##if "MCH01BR" in site:
##    path = "ManchesterB"
##elif "MCH01SR" in site:
##    path = "ManchesterS"
##elif "MUK01BR" in site:
##    path = "MukilteoB"
##elif "MUK01SR" in site:
##    path = "MukilteoS"
##elif "SQX01CF" in site:
##    path = "SquaxinC"
##elif "WPA13CR" in site:
##    path = "Wilapa"
##else:
##    path = "Site code not recognized.  Terminating"
##path = site

if "Terminating" not in Path:
    fn = site + strftime('_Day%d%m%YTime%H%M', gmtime())
    Path = op.join(dest, Path)
    path = op.join(Path, fn)
    try:
        if writeOut:
            out = open(path, "w")
            out.write(site + os.linesep)
            gd = GD.getdata((ip, port), out, logging, Max, 1) # Instantiate the getdata object
        else:
            gd = GD.getdata((ip, port), None, logging, Max, 0) # Instantiate the getdata object
        #print "getdata object created, attmpting to open telnet conn."
        for tries in range(5):
            try:
                #print 'Try ', tries
                tn23 = T(ip, 23, Max)
                break
            except timeout, to:
                if tries==4:
                    raise to
                else:
                    pass
        #print "Telnet connection opened, attempting mult. ser. port dect/kill"
        progress_code = "Detecting & killing multiple serial ports"
        if gd.writeOut:
            gd.write_progress(progress_code)
        tmp = tn23.expect(["#>", "login:"],  Max)[2]
        if "login:" in tmp:
            tn23.write("admin\r\n")
            tmp = tn23.read_until("password:", Max)
            tn23.write("strix!12\r\n")
            tn23.read_until("#>", Max)
        progress_code = "Logged in or login not needed"
        if gd.writeOut:
            gd.write_progress(progress_code)
        pids = get_serial_ports(tn23)
        npids = len(pids)
        tries = 0
        while npids and (tries < Max):
            tries += 1
            kill_serial_ports(tn23, pids)
            pids = get_serial_ports(tn23)
            npids = len(pids)
        if npids==0:
            progress_code = "Multiple serial ports killed or never existed"
            if gd.writeOut:
                gd.write_progress(progress_code)
        else:
            progress_code = site + " timed out trying to kill multiple serial ports.  "
            progress_code += "Giving up.  Note: data logging should not have stopped."
            raise kill_timeout
    ### This is where the work gets done ###
        tn, date, time, last_dt = gd.getdata(hours2sample, offset)
    ########################################
        if site != 'HQ':
            if isinstance(tn, T):
                # issue delayed logging restart
                progress_code = "Issuing restart commands"
                if gd.writeOut:
                    gd.write_progress(progress_code)
                restart = figure_out_restart(date, time)
                cmnd = restart.date().strftime("startmmddyy=%m%d%y\r\n")
                tn.write(cmnd)
                progress_code = tn.read_until(prompt, Max)
                if gd.writeOut:
                    gd.write_progress(progress_code)
                cmnd = restart.time().strftime("starthhmmss=%H%M%S\r\n")
                tn.write(cmnd)
                progress_code = tn.read_until(prompt, Max)
                if gd.writeOut:
                    gd.write_progress(progress_code)
                cmnd = "startlater\r\n"
                tn.write(cmnd)
                progress_code = tn.read_until(prompt, Max)
                if gd.writeOut:
                    gd.write_progress(progress_code)
                if "start time =" not in progress_code:
                    progress_code = "Restart failed to read final 'start time ='" + os.linesep
                    progress_code += "Please check to see if " + site + " restarted."
                    report_failure(site, progress_code)
                    if gd.writeOut:
                        gd.write_progress(progress_code)
            elif type(tn) is str:
                progress_code = "The following error was returned trying to telnet download data "
                progress_code += "from " + site + ":" + os.linesep + tn + os.linesep
                progress_code += "This should have taken place after checking for/killing multiple "
                progress_code += "serial ports, but before logging restart commands were issued; "
                progress_code += "please check " + site + " to see if data logging needs to be "
                progress_code += "restarted."
                report_failure(site, progress_code)
                if gd.writeOut:
                    gd.write_progress(progress_code)
                    gd.out.close()
            else:
                raise
    except kill_timeout:
        report_failure(site, progress_code)
        if gd.writeOut:
            gd.write_progress(progress_code)
            gd.out.close()
    except:
        t, v, b = sys.exc_info()
        progress_code += os.linesep + str(t) + str(v)
        progress_code += os.linesep + "The above unhandled exception prematurely aborted "
        progress_code += "telnet data download from " + site + "." + os.linesep
        progress_code += "Tries to open telnet connection = " + str(tries) + os.linesep
        progress_code += "It is unknown if data logging was stopped at the time of failure-"
        progress_code += "please check manually."
        report_failure(site, progress_code)
        if "gd" in dir():
            try:
                gd.write_progress(progress_code)
                gd.out.close()
            except IOError:
                pass
    finally:
        if "tn23" in dir(): tn23.close()
        if "tn" in dir(): tn.close()
        #print progress_code
if "tn" in dir(): tn.close()
if "tn23" in dir(): tn23.close()
if "gd" in dir() and gd.writeOut:
    if not gd.out.closed: gd.out.close()
progress_code = site + " Done!"
print Path, fn
#    return progress_code
#print progress_code, last_dt
#driver(sys.argv[1])